package com.hunter.lucene.util.config.validation;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Field.TermVector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hunter.lucene.util.config.AnalyzerHolder;
import com.hunter.lucene.util.config.DiscriminatorValue;
import com.hunter.lucene.util.config.DocumentConfigInfo;
import com.hunter.lucene.util.config.FieldableConfigInfo;
import com.hunter.lucene.util.config.MappingConfigInfoHolder;
import com.hunter.lucene.util.config.UndocumentConfigInfo;
import com.hunter.lucene.util.config.UnfieldableConfigInfo;

/**
 * 验证配置信息。 进行以下验证：
 * <ul>
 * <li>默认的分词器，必须存在。</li>
 * <li>每个Document 指定的分词器必须存在。</li>
 * <li>每个Document 的 Discriminator 必须不同</li>
 * <li>Document 中的所有 Field 和Discriminator(特殊的Field)的 name 必须不同</li>
 * <li>每个 Undocument 中的所有 Discriminator 必须不同</li>
 * </ul>
 * 
 * @author bastengao
 */
public class ConfigInfoValidator {

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	public static final EnumSet<TermVector> YES_TERM_VECTOR = EnumSet.of(
			TermVector.YES, TermVector.WITH_OFFSETS, TermVector.WITH_POSITIONS,
			TermVector.WITH_POSITIONS_OFFSETS);
	private AnalyzerHolder analyzerHolder;
	private MappingConfigInfoHolder mappingConfigInfoHolder;

	public ConfigInfoValidator(AnalyzerHolder analyzerHolder,
			MappingConfigInfoHolder mappingConfigInfoHolder) {
		super();
		if (analyzerHolder == null) {
			throw new IllegalArgumentException("analyzerHolder is null.");
		}
		if (mappingConfigInfoHolder == null) {
			throw new IllegalArgumentException(
					"mappingConfigInfoHolder is null.");
		}

		this.analyzerHolder = analyzerHolder;
		this.mappingConfigInfoHolder = mappingConfigInfoHolder;
	}

	public void validate() throws ConfigInfoInvalideException {
		logger.debug("validating...");
		validateAnalyzer(analyzerHolder);

		Set<DiscriminatorValue> discValueSet = new HashSet<DiscriminatorValue>();
		for (DocumentConfigInfo documentConfigInfo : mappingConfigInfoHolder
				.getDocumentConfigs().values()) {
			validateDocumentConfigInfo(documentConfigInfo, analyzerHolder,
					discValueSet);
		}
		discValueSet = new HashSet<DiscriminatorValue>();
		for (UndocumentConfigInfo undocumentConfigInfo : mappingConfigInfoHolder
				.getUndocumentConfigs().values()) {
			validateUndocumentConfigInfo(undocumentConfigInfo, discValueSet);
		}
	}

	protected void validateAnalyzer(AnalyzerHolder analyzerHolder)
			throws ConfigInfoInvalideException {
		String defaultAnalyzerName = analyzerHolder.getDefaultAnalyzerName();
		if (defaultAnalyzerName == null) {
			throw new ConfigInfoInvalideException(
					"default analyzer name is null at \"default\" attribute of  \"analyzers\" element");
		}

		if (defaultAnalyzerName.equals("")) {
			throw new ConfigInfoInvalideException(
					"default analyzer name is empty at \"default\" attribute of  \"analyzers\" element");
		}

		Analyzer defaultAnalyzer = analyzerHolder.get(defaultAnalyzerName);
		if (defaultAnalyzer == null) {
			throw new ConfigInfoInvalideException(
					"default analyzer is not provided.");
		}
	}

	protected void validateDocumentConfigInfo(
			DocumentConfigInfo documentConfigInfo,
			AnalyzerHolder analyzerHolder, Set<DiscriminatorValue> discValues)
			throws ConfigInfoInvalideException {
		String analyzerName = documentConfigInfo.analyzerName();
		if (analyzerName == null) {
			throw new ConfigInfoInvalideException(
					"analyzerName is not privided ");
		}

		if (analyzerName.equals("")) {

		}
		Analyzer analyzer = analyzerHolder.get(analyzerName);
		if (analyzer == null) {
			throw new ConfigInfoInvalideException("analyzer named \""
					+ analyzerName + "\" is not available.");
		}

		DiscriminatorValue disc = documentConfigInfo.discriminator();

		if (disc == null) {
			throw new ConfigInfoInvalideException("discrimination is null");
		}

		if (discValues.contains(disc)) {
			throw new ConfigInfoInvalideException(
					"the disciminations of documents msut be different." + disc);
		} else {
			discValues.add(disc);
		}

		if (disc.getName() == null) {
			throw new ConfigInfoInvalideException(
					"the name of discrimination is null.");
		}

		if (disc.getName().equals("")) {
			throw new ConfigInfoInvalideException(
					"the name of discrimination is empty.");
		}

		if (disc.getValue() == null) {
			throw new ConfigInfoInvalideException(
					"the value of discrimination is null.");
		}

		if (disc.getValue().equals("")) {
			throw new ConfigInfoInvalideException(
					"the value of discrimination is empty.");
		}

		Set<String> fieldNameSet = new HashSet<String>();

		fieldNameSet.add(disc.getName());
		for (FieldableConfigInfo fieldConfigInfo : documentConfigInfo
				.fieldConfigs()) {
			validateFieldConfigInfo(fieldConfigInfo, fieldNameSet);
		}
	}

	/**
	 * <ul>
	 * <li>
	 * the field is neither stored nor indexed</li>
	 * <li>
	 * the field is not indexed but termVector is TermVector.YES</li>
	 * 
	 * @param fieldConfigInfo
	 * @throws ConfigInfoInvalideException
	 */
	protected void validateFieldConfigInfo(FieldableConfigInfo fieldConfigInfo,
			Set<String> fieldNameSet) throws ConfigInfoInvalideException {
		if (fieldConfigInfo.name() == null) {
			throw new ConfigInfoInvalideException("field name is null");
		}

		if (fieldNameSet.contains(fieldConfigInfo.name())) {
			throw new ConfigInfoInvalideException(
					"field name is already used :\"" + fieldConfigInfo.name()
							+ "\"");
		} else {
			fieldNameSet.add(fieldConfigInfo.name());
		}

		Method readMethod = fieldConfigInfo.readMethod();
		if (readMethod == null) {
			throw new ConfigInfoInvalideException("read method is null");
		}

		if (!Modifier.isPublic(readMethod.getModifiers())) {
			throw new ConfigInfoInvalideException("the method is not public :"
					+ readMethod.getName());
		}

		if (fieldConfigInfo.convertor() == null) {
			throw new ConfigInfoInvalideException(
					"the convertor of field is null");
		}

		Store store = fieldConfigInfo.store();
		Index index = fieldConfigInfo.index();
		TermVector termVector = fieldConfigInfo.termVector();
		if (store == Store.NO && index == Index.NO) {
			throw new ConfigInfoInvalideException(
					"the field is neither stored nor indexed");
		}

		if (index == Index.NO && YES_TERM_VECTOR.contains(termVector)) {
			throw new ConfigInfoInvalideException(
					"the field is not indexed but termVector is TermVector.YES");
		}
	}

	protected void validateUndocumentConfigInfo(
			UndocumentConfigInfo undocumentConfigInfo,
			Set<DiscriminatorValue> discValues)
			throws ConfigInfoInvalideException {
		DiscriminatorValue discValue = undocumentConfigInfo.discrimination();
		if (discValue == null) {
			throw new ConfigInfoInvalideException("discrimination is null");
		}

		if (discValue.getName() == null) {
			throw new ConfigInfoInvalideException(
					"the name of discrimination is null.");
		}

		if (discValue.getName().equals("")) {
			throw new ConfigInfoInvalideException(
					"the name of discrimination is empty.");
		}

		if (discValue.getValue() == null) {
			throw new ConfigInfoInvalideException(
					"the value of discrimination is null.");
		}

		if (discValue.getValue().equals("")) {
			throw new ConfigInfoInvalideException(
					"the value of discrimination is empty.");
		}

		if (discValues.contains(discValue)) {
			throw new ConfigInfoInvalideException(
					"the discriminators of undocumetns must be different "
							+ discValue);
		} else {
			discValues.add(discValue);
		}

		for (UnfieldableConfigInfo unfieldConfigInfo : undocumentConfigInfo
				.unfieldableConfigs()) {
			validateUnfieldConfigInfo(unfieldConfigInfo);
		}

	}

	protected void validateUnfieldConfigInfo(

	UnfieldableConfigInfo unfieldConfigInfo) throws ConfigInfoInvalideException {
		
		if (unfieldConfigInfo.name() == null) {
			throw new ConfigInfoInvalideException("name of unfield is null");
		}

		if (unfieldConfigInfo.name().equals("")) {
			throw new ConfigInfoInvalideException("name of unfield is empty.");
		}

		if (unfieldConfigInfo.convertor() == null) {
			throw new ConfigInfoInvalideException("convertor is null");
		}

		Method writeMethod = unfieldConfigInfo.writeMethed();

		if (writeMethod == null) {
			throw new ConfigInfoInvalideException("write method is null");
		}
	}
}
